package com.matrix.im.application;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.matrix.api.im.dto.OrderCardDTO;
import com.matrix.api.im.dto.SendOrderMessageDTO;
import com.matrix.api.im.vo.ChatMessageVO;
import com.matrix.api.im.vo.ConversationVO;
import com.matrix.api.order.OrderApi;
import com.matrix.api.user.UserApi;
import com.matrix.common.domain.PageResult;
import com.matrix.common.enums.UserTypeEnum;
import com.matrix.common.exception.BusinessException;
import com.matrix.im.converter.ImConverter;
import com.matrix.im.domain.model.ChatMessage;
import com.matrix.im.domain.model.Conversation;
import com.matrix.im.domain.repository.ConversationRepository;
import com.matrix.im.domain.service.MessageFactory;
import com.matrix.im.infrastructure.event.handler.CouponGrantingEventHandler;
import com.matrix.im.infrastructure.messaging.MessageBus;
import com.matrix.im.interfaces.dto.SendMessageDTO;
import com.matrix.im.utils.XssSanitizerUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author 有点甜
 * @since 2025/8/2
 */
@Slf4j
@Service
public class IMAppService {

    private final ConversationRepository conversationRepository;
    private final MessageFactory messageFactory;
    private final MessageBus messageBus;
    private final CouponGrantingEventHandler couponGrantingEventHandler;
    private final ObjectMapper objectMapper;
    private final IMAppService self;

    @DubboReference(check = false, timeout = 10000)
    private OrderApi orderApi;

    @DubboReference(check = false)
    private UserApi userApi;

    public IMAppService(ConversationRepository conversationRepository,
                        MessageFactory messageFactory,
                        MessageBus messageBus,
                        CouponGrantingEventHandler couponGrantingEventHandler,
                        ObjectMapper objectMapper,
                        @Lazy IMAppService self) {
        this.conversationRepository = conversationRepository;
        this.messageFactory = messageFactory;
        this.messageBus = messageBus;
        this.couponGrantingEventHandler = couponGrantingEventHandler;
        this.objectMapper = objectMapper;
        this.self = self;
    }

    @Transactional
    public void sendMessage(Long fromUserId, SendMessageDTO dto) {
        validateChatPermission(fromUserId, dto.getToUserId(), dto.getBody().getType());
        Conversation conversation = conversationRepository.findOrCreateByUsers(fromUserId, dto.getToUserId());

        SendMessageDTO.MessageBodyDTO body = dto.getBody();
        if ("TEXT".equalsIgnoreCase(body.getType())) {
            String sanitizedContent = XssSanitizerUtil.sanitize(body.getContent());
            body.setContent(sanitizedContent);
        }

        String messageContentJson;
        try {
            messageContentJson = objectMapper.writeValueAsString(dto.getBody());
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize message body from user {}", fromUserId, e);
            throw new BusinessException("消息内容格式错误");
        }

        ChatMessage message = messageFactory.createMessage(
                conversation.getId(),
                fromUserId,
                dto.getToUserId(),
                messageContentJson,
                dto.getBody().getType()
        );

        conversation.receiveNewMessage(message);

        conversationRepository.save(conversation);

        messageBus.publishPrivateMessage(message);

        if ("CUSTOM_COUPON".equals(message.getMessageType())) {
            couponGrantingEventHandler.handle(fromUserId, message.getToUserId(), dto.getBody().getContent());
        }
    }

    public PageResult<ChatMessageVO> getChatHistory(Long userId, Long friendId, int pageNum, int pageSize) {
        Page<ChatMessage> page = conversationRepository.findChatMessages(userId, friendId, pageNum, pageSize);
        List<ChatMessageVO> voList = page.getContent().stream()
                .map(ImConverter.INSTANCE::toChatMessageVO)
                .collect(Collectors.toList());
        return new PageResult<>(voList, page.getTotalElements(), page.getTotalPages());
    }

    public PageResult<ConversationVO> getConversations(Long userId, int pageNum, int pageSize) {
        Page<ConversationVO> page = conversationRepository.findConversationsByUserId(userId, pageNum, pageSize);
        return new PageResult<>(page.getContent(), page.getTotalElements(), page.getTotalPages());
    }

    @Transactional
    public void markConversationAsRead(Long userId, Long friendId) {
        Conversation conversation = conversationRepository.findOrCreateByUsers(userId, friendId);
        conversation.markAsRead(userId);
        conversationRepository.save(conversation);
    }

    public void sendOrderMessage(Long fromUserId, SendOrderMessageDTO dto) {
        validateChatPermission(fromUserId, dto.getToUserId(), "ORDER_CARD");
        OrderCardDTO orderCard = orderApi.getOrderCardInfo(dto.getOrderId(), fromUserId);

        String contentJson;
        try {
            contentJson = objectMapper.writeValueAsString(orderCard);
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize OrderCardDTO for orderId {}", dto.getOrderId(), e);
            throw new BusinessException("发送订单消息失败，请稍后重试");
        }

        SendMessageDTO genericMessage = new SendMessageDTO();
        genericMessage.setToUserId(dto.getToUserId());
        genericMessage.setClientMessageId(dto.getClientMessageId());

        SendMessageDTO.MessageBodyDTO body = new SendMessageDTO.MessageBodyDTO();
        body.setType("ORDER_CARD");
        body.setContent(contentJson);
        genericMessage.setBody(body);

        self.sendMessage(fromUserId, genericMessage);
    }

    private void validateChatPermission(Long fromUserId, Long toUserId, String messageType) {
        Map<Long, UserTypeEnum> userTypes = userApi.getUserTypesByIds(List.of(fromUserId, toUserId));

        UserTypeEnum fromUserType = userTypes.get(fromUserId);
        UserTypeEnum toUserType = userTypes.get(toUserId);

        if (fromUserType == null || toUserType == null) {
            throw new BusinessException("用户不存在");
        }

        if (fromUserType == UserTypeEnum.USER && toUserType == UserTypeEnum.USER) {
            throw new BusinessException("不允许用户间私聊");
        }

        if (fromUserType == UserTypeEnum.MERCHANT && toUserType == UserTypeEnum.MERCHANT) {
            throw new BusinessException("不允许商家间私聊");
        }

        if ("ORDER_CARD".equalsIgnoreCase(messageType)) {
            if (fromUserType != UserTypeEnum.USER || toUserType != UserTypeEnum.MERCHANT) {
                throw new BusinessException("只能由用户向商家发送订单");
            }
        }
    }
    public long getTotalUnreadCount(Long userId) {
        return conversationRepository.getTotalUnreadCount(userId);
    }
}
